// Write C code here.
//
// Do not forget to dynamically load the C library into your application.
//
// For instance,
//
// In MainActivity.java:
//    static {
//       System.loadLibrary("lib-handle");
//    }
//
// Or, in MainActivity.kt:
//    companion object {
//      init {
//         System.loadLibrary("lib-handle")
//      }
//    }
#include <jni.h>
//#include <string>

#include <string.h>
#include <stdlib.h>
//#include <libavutil/log.h>
#include "libum.h"
#include "json/cJSON.h"
#include "ffmpeg/ffmpeg.h"
#include "ffmpeg_jni_define.h"

//using namespace std;JNI DETECTED ERROR IN APPLICATION: JNI GetMethodID called with pending exception java.lang.NoSuchMethodError: no non-static method "Lcom/zj/musicTools/function/ffmpegHandle;.onPCallback(III)V"
#define INPUT_SIZE (8 * 1024)
extern int FLAC_To_WAV(const char* INPUT_FILE,const char* OUTPUT_FILE);

//jstring to char*
char* jstringToString(JNIEnv* env, jstring jstr)
{
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env,"java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env,"utf-8");
    jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode);
    jsize alen = (*env)->GetArrayLength(env,barr);
    jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    (*env)->ReleaseByteArrayElements(env,barr, ba, 0);
    return rtn;
}
/*

//char* to jstring
jstring toJstring(JNIEnv* env, const char* pat)
{
    jclass strClass = env->FindClass("java/lang/String");
    jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = env->NewByteArray(strlen(pat));
    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
    jstring encoding = env->NewStringUTF("utf-8");
    return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}
*/


//extern "C"
JNIEXPORT jstring JNICALL
Java_com_zj_musicTools_function_AgentStation_decryptMusic(JNIEnv *env, jobject clazz, jstring SRC, jstring DST) {
    JNIEnv *d_env = env;
/*        err_count = 0;
    ff_class = (*env)->FindClass(env, "com/zj/musicTools/function/AgentStation");  // 类所在路径
    ff_method = (*env)->GetMethodID(env, ff_class, "onProgressCallback", "(III)V");  //方法名
    msg_method = (*env)->GetMethodID(env, ff_class, "onMsgCallback", "(Ljava/lang/String;)V"); //方法名*/

    // TODO: implement tryDecFile()
    char *in, *out;
        in=jstringToString(d_env,SRC);
        out=jstringToString(d_env,DST);

    struct tryDecFile_return backdata =tryDecFile(in, out);

    char *backjson=NULL;/*按照既定格式输出log*/

    cJSON *root;
    root=cJSON_CreateObject();
    backdata.r0?cJSON_AddTrueToObject (root, "result"):cJSON_AddFalseToObject (root, "result");
    cJSON_AddStringToObject(root, "data", backdata.r1);
    backjson=cJSON_Print(root);
    // string result=backjson;//env->NewStringUTF(backjson)
    cJSON_Delete(root);

    return (*d_env)->NewStringUTF(d_env,backjson);
}

/*
extern "C"
JNIEXPORT jstring JNICALL
Java_com_zj_musicTools_function_AgentStation_ffmpeg(JNIEnv *env, jobject clazz,jstring SRC,jstring DST) {
    //ffmpeg 实现函数


    *//**
    参考实现
    https://blog.csdn.net/qq_36351159/article/details/127664085
    官方示例
    https://ffmpeg.org/doxygen/trunk/examples.html
    *//*


    char *in,*out;
    in=jstringToString(env,SRC);
    out=jstringToString(env,DST);
*//*
    struct tryDecFile_return backdata =tryDecFile(in, out);

    std::string backjson;
    cJSON *root;
    root=cJSON_CreateObject();
    backdata.r0?cJSON_AddTrueToObject (root,"result"):cJSON_AddFalseToObject (root,"result");
    cJSON_AddStringToObject(root, "data", backdata.r1);
    backjson=cJSON_Print(root);

    return env->NewStringUTF(backjson.c_str());
*//*
std::string hello = "Hello from C++";
//    return env->NewStringUTF(hello.c_str());
//    int ret = FLAC_To_WAV(in,out);
  //  const char* ret = transcode(in,out);
  int ret =decode_flac(in,out);
//    std::string buf=code_trans(in,out);

    if(ret == 0)
    hello = "output WAV succeed";
    else
    hello = "output WAV failed";

    return env->NewStringUTF(
//            buf.c_str()
    hello.c_str()
   // avcodec_configuration()
    );
}*/
//jniHandler

/* void log_callback(void *ptr, int level, const char *format, va_list args) {
    switch (level) {
        case AV_LOG_INFO:
            ALOGI(FFMPEG_TAG, format, args);
            if (format && strncmp("silence", format, 7) == 0) {
            msg_callback(format, args, 3);
            }
            break;
        case AV_LOG_ERROR:
            ALOGE(FFMPEG_TAG, format, args);
            if (err_count < 10) {
            err_count++;
            msg_callback(format, args, 6);
            }
            break;
        default:
        break;
    }
} */
#define FFMPEG_TAG "FFmpegjni"
#define ALOGI(TAG, FORMAT, ...) __android_log_vprint(ANDROID_LOG_INFO, TAG, FORMAT, ##__VA_ARGS__)
#define ALOGE(TAG, FORMAT, ...) __android_log_vprint(ANDROID_LOG_ERROR, TAG, FORMAT, ##__VA_ARGS__)


int err_count;
JNIEnv *ff_env;
jclass ff_class;
jmethodID ff_method;
jmethodID msg_method;

void log_callback(void *, int, const char *, va_list);

void init(JNIEnv *env) {
    ff_env = env;
    err_count = 0;
    ff_class = (*env)->FindClass(env, "com/zj/musicTools/function/AgentStation");  // 类所在路径
    ff_method = (*env)->GetStaticMethodID(env, ff_class, "onProgressCallback", "(III)V");  //方法名
    msg_method = (*env)->GetStaticMethodID(env, ff_class, "onMsgCallback", "(Ljava/lang/String;I)V"); //方法名
}

//extern "C"
JNIEXPORT jint JNICALL
Java_com_zj_musicTools_function_AgentStation_ffmpeg(JNIEnv *env, jobject clazz, jobjectArray commands) {
    init(env);
    // set the level of log
    av_log_set_level(AV_LOG_INFO);
    // set the callback of log, and redirect to print android log
    av_log_set_callback(log_callback);

    int argc = (*env)->GetArrayLength(env, commands);
    char **argv = (char **) malloc(argc * sizeof(char *));
    int i;
    int result;
    for (i = 0; i < argc; i++) {
        jstring jstr = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
        char *temp = (char *) (*env)->GetStringUTFChars(env, jstr, 0);
        argv[i] = malloc(INPUT_SIZE);
        strcpy(argv[i], temp);
        (*env)->ReleaseStringUTFChars(env, jstr, temp);
    }
    //execute ffmpeg cmd
    result = run(argc, argv);
    //release memory
    for (i = 0; i < argc; i++) {
        free(argv[i]);
    }
    free(argv);
    return result;
}

//extern "C"
JNIEXPORT void JNICALL
Java_com_zj_musicTools_function_AgentStation_FcancelTask(JNIEnv *env, jobject clazz, jint cancel) {
    cancel_task(cancel);
}

void msg_callback(const char *format, va_list args, int level) {
    if (ff_env && ff_class && ff_method) {
        char *ff_msg = (char *) malloc(sizeof(char) * INPUT_SIZE);
        if (!ff_msg)
            return;

//        char *backjson;/*按照既定格式输出log*/
//        cJSON *root;
//        root=cJSON_CreateObject();
//        (level==3)?cJSON_AddTrueToObject (root,"result"):cJSON_AddFalseToObject (root,"result");

        vsprintf(ff_msg, format, args);
//        jstring jstr = (*ff_env)->NewStringUTF(ff_env, ff_msg);

//        if (ff_msg && root){
//            cJSON_AddStringToObject(root, "data", ff_msg);
//            backjson=cJSON_Print(root);
//            // string result=backjson;//env->NewStringUTF(backjson)
//            cJSON_Delete(root);
//            if(backjson)(*ff_env)->CallStaticVoidMethod(ff_env, ff_class, msg_method, (*ff_env)->NewStringUTF(ff_env,backjson));//反向调用java代码
//        }
        (*ff_env)->CallStaticVoidMethod(ff_env,ff_class,msg_method,((*ff_env)->NewStringUTF(ff_env,ff_msg)),level);//反向调用java代码
        free(ff_msg);
    }
}

void log_callback(void *ptr, int level, const char *format, va_list args) {
    switch (level) {
        case AV_LOG_INFO:
//            ALOGI(FFMPEG_TAG, format, args);
//             Agent.log()
            if (format) {
                msg_callback(format, args, 3);
            }
            break;
        case AV_LOG_ERROR:
//            ALOGE(FFMPEG_TAG, format, args);
            if (err_count < 10) {
                err_count++;
                msg_callback(format, args, 6);
            }
            break;
        default:

            break;
    }
}

void progress_callback(int position, int duration, int state) {
    if (ff_env && ff_class && ff_method) {
        (*ff_env)->CallStaticVoidMethod(ff_env, ff_class, ff_method, position, duration, state);
    }
}
